
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics.CodeAnalysis;

namespace Bookvar
{
	public static class Shell
	{
		private const string updateExeFileName = "Bookvar.UpdateMonitor.exe";

		private delegate int ExecuteHandOff( IWin32Window parent, string exePath, string args, out IntPtr hProcess );

		public static void Execute(
			IWin32Window parent,
			string exePath,
			string args )
		{
			if ( exePath == null )
			{
				throw new ArgumentNullException( "exePath" );
			}
			ExecuteHandOff executeHandOff = null;
			executeHandOff = ExecRequireAdmin;

			string updateMonitorExePath = null;

			CopyUpdateMonitorToTempFolder( out updateMonitorExePath );


			IntPtr hProcess = IntPtr.Zero;
			int nResult = executeHandOff( parent, exePath, args, out hProcess );

			if ( nResult == NativeConstants.ERROR_SUCCESS )
			{
				bool bResult2 = SafeNativeMethods.SetHandleInformation(
					hProcess,
					NativeConstants.HANDLE_FLAG_INHERIT,
					NativeConstants.HANDLE_FLAG_INHERIT );

				RelaunchBookvar( updateMonitorExePath, hProcess );
			}
			else
			{
				if ( updateMonitorExePath != null )
				{
					File.Delete( updateMonitorExePath );
				}
			}

			GC.KeepAlive( parent );
		}

		private static int ExecRequireAdmin( IWin32Window parent, string exePath, string args, out IntPtr hProcess )
		{
			//const string runAs = "runas";

			string verb = null ;
//			string verb = IsAdministrator ? null : runAs;

			return ExecShellExecuteEx( parent, exePath, args, verb, out hProcess );
		}

		[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private static int ExecShellExecuteEx(IWin32Window parent, string exePath, string args, string verb, out IntPtr hProcess)
        {
            string dir;

            try
            {
                dir = Path.GetDirectoryName(exePath);
            }

            catch (Exception)
            {
                dir = null;
            }

            NativeStructs.SHELLEXECUTEINFO sei = new NativeStructs.SHELLEXECUTEINFO();
            sei.cbSize = (uint)Marshal.SizeOf(typeof(NativeStructs.SHELLEXECUTEINFO));

            sei.fMask =
                NativeConstants.SEE_MASK_NOCLOSEPROCESS |
                NativeConstants.SEE_MASK_NO_CONSOLE |
                NativeConstants.SEE_MASK_FLAG_DDEWAIT;

            sei.lpVerb = verb;
            sei.lpDirectory = dir;
            sei.lpFile = exePath;
            sei.lpParameters = args;

            sei.nShow = NativeConstants.SW_SHOWNORMAL;

            if (parent != null)
            {
                sei.hwnd = parent.Handle;
            }

            bool bResult = NativeMethods.ShellExecuteExW(ref sei);
            hProcess = sei.hProcess;
            sei.hProcess = IntPtr.Zero;

            int nResult = NativeConstants.ERROR_SUCCESS;

            if (!bResult)
            {
                nResult = Marshal.GetLastWin32Error();    
            }

            return nResult;
        }

		private static void CopyUpdateMonitorToTempFolder( out string updateMonitorExePath )
		{
			string srcDir = AppDomain.CurrentDomain.BaseDirectory;
			string srcPath = Path.Combine( srcDir, updateExeFileName );

			string dstDir = Environment.ExpandEnvironmentVariables( @"%TEMP%\BookvarSetup" );
			string dstPath = Path.Combine( dstDir, updateExeFileName );

			if ( !Directory.Exists( dstDir ) )
			{
				Directory.CreateDirectory( dstDir );
			}

			File.Copy( srcPath, dstPath, true );
			updateMonitorExePath = dstPath;
		}

		private static void RelaunchBookvar( string updateMonitorExePath, IntPtr hProcess )
		{
			string args = hProcess.ToInt64().ToString( CultureInfo.InstalledUICulture );
			ProcessStartInfo psi = new ProcessStartInfo( updateMonitorExePath, args );
			psi.UseShellExecute = false;
			psi.WindowStyle = ProcessWindowStyle.Hidden;
			Process process = Process.Start( psi );
			process.Dispose();
		}


		//private static bool IsAdministrator
		//{
		//    get
		//    {
		//        AppDomain domain = Thread.GetDomain();
		//        domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
		//        IPrincipal principal = Thread.CurrentPrincipal;
		//        return principal.IsInRole("Administrator");
		//    }
		//}
	}
}